/**
 * Copyright (c) 2018-2025, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: syscall trace event
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2025-06-24     JasonHu           Init
 */

#include <trace/syscall.h>
#include <base/trace.h>
#include <base/debug.h>
#include <base/initcall.h>

#define NX_LOG_NAME "trace-syscall"
// #define NX_LOG_LEVEL NX_LOG_DBG
#include <base/log.h>

#ifdef CONFIG_TRACE_EVENT_SYSCALL

#define SYS_TRACE_EVENT_NAME "Syscall"

#define SYS_TRACE_LOG_NR 124

NX_PRIVATE NX_TraceEvent *SyscallTraceEvent;

NX_PRIVATE NX_EVENT_SOURCE_DEFINE(SyscallEnter);
NX_PRIVATE NX_EVENT_SOURCE_DEFINE(SyscallExit);

#define SYSCALL_ENTER_REGS_NR (8)
#define SYSCALL_EXIT_REGS_NR (2)

NX_Error NX_TraceSyscallEnter(NX_UArch syscallNr, NX_UArch arg0, NX_UArch arg1, NX_UArch arg2,
    NX_UArch arg3, NX_UArch arg4, NX_UArch arg5, NX_UArch arg6)
{
    NX_EVENT_HEADER_DEFINE(hdr);
    NX_UArch regs[SYSCALL_ENTER_REGS_NR];

    /* ignore trace self */
    if (syscallNr == SYS_TRACE_LOG_NR)
    {
        return NX_EAGAIN;
    }

    if (NX_EventSourceDisabled(NX_EVENT_SOURCE_STATIC(SyscallEnter)))
    {
        return NX_EAGAIN;
    }

    hdr.level = 1;
    hdr.task = NX_ThreadSelf()->tid;
    hdr.version = 1;
    hdr.keyword = 1;

    regs[0] = syscallNr;
    regs[1] = arg0;
    regs[2] = arg1;
    regs[3] = arg2;
    regs[4] = arg3;
    regs[5] = arg4;
    regs[6] = arg5;
    regs[7] = arg6;
    
    return NX_TraceEventWrite(NX_EVENT_SOURCE_STATIC(SyscallEnter), &hdr, regs, sizeof(regs));
}

NX_Error NX_TraceSyscallExit(NX_UArch syscallNr, NX_UArch value)
{
    NX_EVENT_HEADER_DEFINE(hdr);
    NX_UArch regs[SYSCALL_EXIT_REGS_NR];

    /* ignore trace self */
    if (syscallNr == SYS_TRACE_LOG_NR)
    {
        return NX_EAGAIN;
    }

    if (NX_EventSourceDisabled(NX_EVENT_SOURCE_STATIC(SyscallExit)))
    {
        return NX_EAGAIN;
    }

    hdr.level = 1;
    hdr.task = NX_ThreadSelf()->tid;
    hdr.version = 1;
    hdr.keyword = 2;

    regs[0] = syscallNr;
    regs[1] = value;
    
    return NX_TraceEventWrite(NX_EVENT_SOURCE_STATIC(SyscallExit), &hdr, regs, sizeof(regs));
}

NX_PRIVATE void InitSyscallTraceEvent(void)
{
    SyscallTraceEvent = NX_TraceEventCreate(SYS_TRACE_EVENT_NAME, 65536);
    if (!SyscallTraceEvent)
    {
        NX_LOG_E("Create trace event %s failed!", SYS_TRACE_EVENT_NAME);
        return;
    }
    NX_TraceEventEnable(SyscallTraceEvent);
    NX_LOG_I("Create trace event %s suceess!", SYS_TRACE_EVENT_NAME);

    if (NX_EventSourceRegister(NX_EVENT_SOURCE_STATIC(SyscallEnter), SYS_TRACE_EVENT_NAME) != NX_EOK)
    {
        NX_TraceEventDestroy(SyscallTraceEvent);
        SyscallTraceEvent = NX_NULL;
        return;
    }
    NX_EventSourceEanble(NX_EVENT_SOURCE_STATIC(SyscallEnter), NX_True);
    NX_LOG_I("Create event suorce SyscallEnter suceess!");

    if (NX_EventSourceRegister(NX_EVENT_SOURCE_STATIC(SyscallExit), SYS_TRACE_EVENT_NAME) != NX_EOK)
    {
        NX_EventSourceUnregister(NX_EVENT_SOURCE_STATIC(SyscallEnter));
        NX_TraceEventDestroy(SyscallTraceEvent);
        SyscallTraceEvent = NX_NULL;
        return;        
    }
    NX_EventSourceEanble(NX_EVENT_SOURCE_STATIC(SyscallExit), NX_True);
    NX_LOG_I("Create event suorce SyscallExit suceess!");
}

NX_PRIVATE void ExitSyscallTraceEvent(void)
{
    if (!SyscallTraceEvent)
    {
        NX_LOG_I("The trace event %s invalid", SYS_TRACE_EVENT_NAME);
        return;
    }

    NX_EventSourceUnregister(NX_EVENT_SOURCE_STATIC(SyscallExit));
    NX_EventSourceUnregister(NX_EVENT_SOURCE_STATIC(SyscallEnter));

    if (NX_TraceEventDestroy(SyscallTraceEvent) != NX_EOK)
    {
        NX_LOG_E("Destroy trace event %s failed!", SYS_TRACE_EVENT_NAME);
    }
}

NX_MODS_INIT(InitSyscallTraceEvent);
NX_MODS_EXIT(ExitSyscallTraceEvent);

#endif